﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using System.Threading;

namespace FriendsNearbyBot.Tests
{
    [TestFixture]
    public class SingleInstaceRunnerFixture
    {
        [Test]
        public void ShouldExecuteWhenNoInstanceAvailable()
        {
            bool normalAction = false;
            bool forceExit = false;
            
            var runner = new SingleInstanceRunner("ShouldExecuteWhenNoInstanceAvailable",
                    () => normalAction = true ,
                    () => forceExit = true
            );

            runner.Run();

            Assert.IsTrue(normalAction);
            Assert.IsFalse(forceExit);
        }

        [Test]
        public void ShouldForceExitWhenInstanceAvailable()
        {
            bool firstInstanceRunning = false;
            #region var firstInstance = ...
            bool firstInstanceTerminate = false;
            var firstInstance = new Thread(new ThreadStart(delegate(){
                var firstRunner = new SingleInstanceRunner("ShouldForceExitWhenInstanceAvailable",
                        delegate() {
                            firstInstanceRunning = true;
                            while(!firstInstanceTerminate){
                                Thread.Sleep(100);
                            }
                        },
                        delegate() { }
                );
                firstRunner.Run();
            }));

            firstInstance.Start();
            #endregion

            // wait first instance.
            while (!firstInstanceRunning) { ; }

            bool normalAction = false;
            bool forceExit = false;

            var runner = new SingleInstanceRunner("ShouldForceExitWhenInstanceAvailable",
                    () => normalAction = true,
                    () => forceExit = true
            );

            runner.Run();

            // terminate first instance
            firstInstanceTerminate = true;

            Assert.IsFalse(normalAction);
            Assert.IsTrue(forceExit);
        }

        [Test]
        public void ShouldDoSequentialExecutions()
        {
            bool normalAction = false;
            bool forceExit = false;

            var firstRunner = new SingleInstanceRunner("ShouldDoSequentialExecutions",
                    () => { },
                    () => { }
            );

            var runner = new SingleInstanceRunner("ShouldDoSequentialExecutions",
                    () => normalAction = true,
                    () => forceExit = true
            );

            firstRunner.Run();
            runner.Run();

            Assert.IsTrue(normalAction);
            Assert.IsFalse(forceExit);
        }

        [Test]
        [ExpectedException(ExpectedMessage="ShouldRethrowExceptionOfNormalAction exception message")]
        public void ShouldRethrowExceptionOfNormalAction()
        {
            var runner = new SingleInstanceRunner("ShouldRethrowExceptionOfNormalAction",
                    () => { throw new Exception("ShouldRethrowExceptionOfNormalAction exception message"); },
                    () => { }
            );

            runner.Run();
        }

        [Test]
        public void ShouldDoSequentialExecutionsIfFirstThrowException()
        {
            bool normalAction = false;
            bool forceExit = false;

            var firstRunner = new SingleInstanceRunner("ShouldDoSequentialExecutionsIfFirstThrowException",
                    () => { throw new Exception("ShouldDoSequentialExecutionsIfFirstThrowException exception message"); },
                    () => { }
            );

            var runner = new SingleInstanceRunner("ShouldDoSequentialExecutionsIfFirstThrowException",
                    () => normalAction = true,
                    () => forceExit = true
            );

            try
            {
                firstRunner.Run();
            }
            catch (Exception e) 
            {
                Assert.AreEqual("ShouldDoSequentialExecutionsIfFirstThrowException exception message", e.Message);
            }

            runner.Run();

            Assert.IsTrue(normalAction);
            Assert.IsFalse(forceExit);
        }
    }
}
